
from datetime import datetime, timedelta

import orjson
from src import settings

from src.db import task_op
from src.db.table import ScheduleTask
from src.enums.exception import ExceptionEnum
from src.enums.task import TaskStatusEnum
from src.models.request import WaybillParams
from src.models.task import TaskUUId, TaskResult
from src.models import response_ok, response_error, ResponseModel
from src.exceptions import ResponseException, ApplicationException
from src.utils import common_util
from starlette import status

class TaskServer:
    # 创建task_uuid
    def create_uuid(self,params: WaybillParams) -> str:
        crawler_info = f"{params.waybill_num.strip()}|{params.uni_code.strip()}"
        return common_util.convert_md5(crawler_info)

    # 创建爬虫任务
    def create(self,request:WaybillParams):
        check_result = self.check_task_params(request)
        if check_result is not True:
            return check_result

        task_uuid = self.create_uuid(request)

        task = task_op.query_by_task_uuid(task_uuid)
        if task is None:
            new_task = ScheduleTask(
                task_uuid=task_uuid,
                task_params=request.model_dump_json(),
            )
            task_op.create(task=new_task)
        return response_ok(
                data = TaskUUId(task_uuid=task_uuid)
            )

    # 执行爬虫任务
    def run(self,task_uuid:str):
        task = task_op.query_by_task_uuid(task_uuid)
        if task is None:
            raise ApplicationException(ExceptionEnum.TASK_UUID_NOT_FOUND_ERROR)
        if task.task_status == TaskStatusEnum.FINISH and datetime.now() - task.exec_time < timedelta(hours=1):
            raise ResponseException(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                error=ExceptionEnum.TASK_EXEC_RATE_TOO_HIGH,
            )
        task.task_status = TaskStatusEnum.EXECUTING.value
        task.crawler_times = 0
        task.exec_time = datetime.now()
        task.reason = None
        task_op.update(task=task)

        if task.task_params is not None:
            task_params = orjson.loads(task.task_params)
        else:
            task_params = None
        crawler_result = TaskResult(
            task_params=task_params,
            task_status=task.task_status,
            reason=None,
            exec_time=task.exec_time.strftime(settings.STANDARD_DATETIME_FORMATTER),
        )
        return response_ok(
            data = crawler_result
        )

    # 删除爬虫任务
    def delete(self,task_uuid: str):
        task_op.delete(task_uuid)
        return response_ok()

    # 获取爬虫结果
    async def get_result(self,task_uuid: str):
        task = task_op.query_by_task_uuid(task_uuid)
        if task is None:
            raise ApplicationException(ExceptionEnum.TASK_UUID_NOT_FOUND_ERROR)

        if task.task_params is not None:
            task_params = orjson.loads(task.task_params)
        else:
            task_params = None

        if task.exec_time is not None:
            exec_time = task.exec_time.strftime(settings.STANDARD_DATETIME_FORMATTER)
        else:
            exec_time = None
        if task.task_status == TaskStatusEnum.FINISH.value:
            result_data = common_util.load_text_from_file(task.data_file_path)
            result_data = orjson.loads(result_data)
        else:
            result_data = None
        crawler_result = TaskResult(
            task_status=task.task_status,
            task_params = task_params,
            exec_time = exec_time,
            reason=task.reason,
            result_data= result_data
        )
        return response_ok(data = crawler_result)

    # 验证爬虫参数
    def check_task_params(self,request:WaybillParams) -> bool | ResponseModel:
        if request is None:
            return response_error(
                error=ExceptionEnum.PARAMS_VALIDATION_FAILED,
                data="task_params is not None"
            )
        if request.uni_code is None or request.waybill_num is None:
            return response_error(
                error=ExceptionEnum.PARAMS_VALIDATION_FAILED,
                data="uni_code or waybill_num is not None"
            )
        if request.uni_code.strip() == "" or request.waybill_num.strip() == "":
            return response_error(
                error=ExceptionEnum.PARAMS_VALIDATION_FAILED,
                data="uni_code or waybill_num is not Empty"
            )

        return True
